Шаг 2. Провести исследовательский анализ данных (EDA)
Шаг 3. Построить модель прогнозирования оттока пользователей
Шаг 4. Сделать кластеризацию пользователей
Шаг 5. Сформулировать выводы и сделать базовые рекомендации по работе с клиентами
# Импортирую нужные библиотеки
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, precision_score, recall_score
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from scipy.cluster.hierarchy import dendrogram, linkage
# Убираем предупреждения об ошибках
import warnings
warnings.simplefilter("ignore")
#Прочитаю и выведу инофрмацию по данным
data = pd.read_csv('gym_churn.csv')
data.info()
data.describe(include='all')
По этим данным можно сделать выводы:
# Приведу колонки к нижнему регистру
data.columns = data.columns.str.lower()
# Проверю на дубликаты
print('Количество дубликатов в данных:',data.duplicated().sum())
# Выведу информацию
display(data.head())
data.tail()
# Посмотрю на уникальные значения столбцов
for column in data.columns:
print(column,":")
print(data[column].unique())
C данными проблем нет.
Нет дулбикатов, нет пропусков, корректный возраст от 18 до 41, нет отрицательных значений. Приступаем к следующему шагу
# Посмотриm на средние значения признаков в двух группах
data.groupby('churn').mean()
Из усреднённых данных видно, что различия есть в признаках. У тех кто ушел в отток признаки меньше.
# Построем гистограммы. Для этого сделаем цикл
sns.set()
for column in data.columns.drop('churn'):
figsize=(8,4)
data.groupby('churn')[column].plot(kind = 'hist',legend=True,bins=20)
plt.legend(['Остались','Ушли'])
plt.xlabel('Распределение признака')
plt.ylabel('Количество')
plt.title(column)
plt.show()
Из графиков видно:
# Построим матрицу корреляции
plt.figure(figsize =(20,10))
sns.heatmap(data.corr(),annot=True,vmax = .7,cmap="BuPu",fmt='0.2f')
plt.show()
Матрица корреляций подтверждает картину на графиках. Больше всего на отток пользователей влияет:
Проживание или работа в районе, где находится фитнес-центр положительно коррелирует с акцией «приведи друга» . Так же положительно коррелируют если клиент сотрудник компании-партнёра клуба с акцией «приведи друга» или с длительностью текущего действующего абонемента
# Разобьем данные на обучающую и валидационную выборку
X = data.drop('churn',axis = 1)
y = data['churn']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,random_state = 0,stratify = y)
Обучим модель на train-выборке двумя способами.
Логистической регрессией и случайным лесом. Для этого сделаю цикл и оцениню метрики accuracy, precision и recall для обеих моделей на валидационной выборке
# Цикл для обучения моделей
for model in [LogisticRegression(random_state=0),RandomForestClassifier(random_state=0)]:
model.fit(X_train,y_train)
y_pred = model.predict(X_test)
print('Accuracy: {:.2f}'.format(accuracy_score(y_test,y_pred)))
print('Precision: {:.2f}'.format(precision_score(y_test,y_pred)))
print('Recall: {:.2f}'.format(recall_score(y_test,y_pred)))
print(" ")
Метрика Accuracy Это доля верно угаданных ответов из всех прогнозов
Precision (Точность) и Recall (полнота) чтобы оценить модель без привязки к соотношению классов.
precision говорит, какая доля прогнозов относительно "1" класса верна. recall показывает, сколько реальных объектов "1" класса вы смогли обнаружить с помощью модели.
Хоть и точность у обоих моделей одинаковая но полнота у логистической регрессии лучше.
Логистической регрессией показала себя лучше чем случайный лес.
# Для начала стандартизируем данные.
scaler = StandardScaler()
# Обучаем и стандартизируем данные
X_st = scaler.fit_transform(X)
X_st # Выведу на экран
# Сделаю матрицу расстояний
linked = linkage(X_st, method = 'ward') # В переменной linked сохранена таблица «связок» между объектами.
# Нарисуем дендрограмму.
plt.figure(figsize=(15, 10)) # Внимание: отрисовка дендрограммы может занять время!
dendrogram(linked, orientation='top')
plt.show()
На основании полученного, можно выделить 4 кластера клиентов. Для более детального рассмотрения можно выделить 6 кластеров клиента(т.к. фиолетовый можно разделить на 3 больших кластера)
# Обучиm модель кластеризации. За число кластеров возьмём n = 5
# задаём модель k_means с числом кластеров 5
km = KMeans(n_clusters = 5, random_state = 0)
# прогнозируем кластеры для наблюдений (алгоритм присваивает им номера от 0 до 4)
labels = km.fit_predict(X_st)
# сохраняем метки кластера в поле нашего датасета
data['cluster'] = labels
# Выведу на экран
data.head()
# выводим статистику по средним значениям наших признаков по кластеру
data.groupby('cluster').mean()
Кластеры почти не отличаются по среднему возрасту и полу.
Кластер 0:
Кластер 1:
Кластеры 2 и 3:
Кластер 4:
data.head(10).T # Переверну
# Сделаю распределение признаков для кластеров
for column in data.columns.drop(['cluster','churn','avg_class_frequency_current_month','avg_class_frequency_total','avg_additional_charges_total','age']):
sns.catplot(x="cluster", y=column, hue="churn",
data=data,
kind="bar", ci=None,legend=False)
plt.title(column)
plt.ylabel(" ")
plt.legend(['Остались','Ушли'],framealpha=.2,loc='best')
plt.show()
# Сделаю распределение признаков для кластеров
for_box = ['avg_class_frequency_current_month','avg_class_frequency_total','avg_additional_charges_total','age']
for column in for_box:
fig = px.box(data,x='cluster', y=column,color='churn',title=column)
fig.show()
Средняя частота посещений в неделю за все время с начала действия абонемента у 3 кластера в оттоке выше чем у тех кто остался. В остальном у тех кто остались показатели выше. только в 2-х кластерах есть 18 летние и они в оттоке. Средний возраст 30 лет, как и писал выше.
# Посчитаем долю оттока
# Посчитаем общее количество клиентов
share = data.groupby('cluster').agg(total=('gender','count')).reset_index()
# Посчитаем количество клиентов которые ушли
share['churn'] = data.groupby('cluster')['churn'].sum()
# Посчитаем долю оттока
share['percent_churn'] = round((share['churn'] / share['total'])* 100,2)
share # Выведем на экран
Самые надежные кластеры это 0 и 4 . У 0 ушло 2.77% а у 4 6.81%. Склонные к оттоку кластеры это 2(44.36%) и 3(51.43%)
Хорошо бы иметь больше данных для построения более качественной модели клиента. Т.к не известно за какой период собраны эти данные, может они уже не актуальны. В какой валюте тратят клиенты деньги, на что именно. По этим параметрам можно составить и доработать модель клиента. Какие групповые занятия посещают и т.к
Самые важные причины, влияющие на отток пользователей, следующие:
Для группы пользователей кластера 0 и 4 лучше ничего не делать, пока показатели не изменяться. Зачем влезать туда что и так хорошо работает
1 кластеру можно предложить новые групповые занятия или скидку,бесплатную услугу от фитнес-центра: кафе, спорт-товары, косметический и массажный салон т.к их показатели высоки и им чего-то не хватает. Здесь надо провести более детальный анализ.
Но этим людям не надо быть навязчивыми это может их отпугнуть т.к они не оставляли свой номер телефона. Это может говорить о том что лишний спам им не нужен.
2 кластер. Это люди которые не проживанию или работают в районе, где находится фитнес-центр. Так же у них низкий показатель акции «приведи друга» и факт посещения групповых занятий. Возможно, им скучно заниматься одним или они находят фитнес-центр поближе к дому или работе. Можно попробовать предложить им побольше групповых занятий, там они могут познакомиться с людьми и вместе ходить в зал и тогда расстояние покажется незначительной проблемой.
3 кластер. Это самая большая группа людей и так же они самая молодая группа. Они посещают реже всех фитнес-центр, может дело в работе, нехватает мотивации или дело в сезоне. Ведь неизвестно когда они уходили. Им можно предложить скидку на абонемент для дополнительной мотивации или сделать упор на другие услуги фитнес-центра, вроде массажа, сауны, бассейна чтобы они могли расслабиться после работы.